﻿using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Net;
using System.Net.Sockets;
using System.Xml;
using System.Data.SqlClient;
using WorkThreading;
using SMS.Manager;
using SMS.BLL;
using SMS.SMPPClient;
using SMS.SMPPClient.PDU;
using SMS.SMPPClient.PDU.Incoming;
using SMS.SMPPClient.PDU.Outgoing;
using SMS.SMPPClient.Util;
using SMS.SMPPClient.Common;

namespace SMS.SMPPClient
{
    public partial class Gateway : Form
    {
        public WorkQueue workMO;
        public WorkQueue workMT;

        public int[] statsMO;
        public int[] statsMT;

        public DateTime nextRefreshTime;
        public TimeSpan refreshInterval;

        public Gateway()
        {
            this.InitializeComponent();

            lblPartnerIP.Text = SMS.BLL.Util.GetAppSettings("PartnerIP").ToString();
            lblMyIP.Text = SMS.BLL.Util.GetAppSettings("MyIP").ToString();

            ConfigSettings.LoadConfig();

            this.nextRefreshTime = DateTime.Now;
            this.refreshInterval = TimeSpan.FromSeconds(0.3);
            this.timerMT.Interval = ConfigApp.ReloadMT;
            
            this.statsMO = new int[6];
            this.statsMT = new int[6];
            this.workMO = new WorkQueue();
            
            ((WorkThreadPool)this.workMO.WorkerPool).MaxThreads = ConfigApp.maxThread;
            ((WorkThreadPool)this.workMO.WorkerPool).MinThreads = ConfigApp.minThread;
            
            this.workMO.ConcurrentLimit = ConfigApp.ConcurrentLimit;
            this.workMO.WorkerException += new ResourceExceptionEventHandler(this.workMO_WorkerException);
            this.workMO.ChangedWorkItemState += new ChangedWorkItemStateEventHandler(this.workMO_ChangedWorkItemState);
            this.workMO.FailedWorkItem += new WorkItemEventHandler(this.workMO_FailedWorkItem);
            this.workMO.AllWorkCompleted += new EventHandler(this.workMO_AllWorkCompleted);
            this.workMT = new WorkQueue();
            
            ((WorkThreadPool)this.workMT.WorkerPool).MaxThreads = ConfigApp.maxThread;
            ((WorkThreadPool)this.workMT.WorkerPool).MinThreads = ConfigApp.minThread;
            
            this.workMT.ConcurrentLimit = ConfigApp.ConcurrentLimit;
            this.workMT.WorkerException += new ResourceExceptionEventHandler(this.workMT_WorkerException);
            this.workMT.ChangedWorkItemState += new ChangedWorkItemStateEventHandler(this.workMT_ChangedWorkItemState);
            this.workMT.FailedWorkItem += new WorkItemEventHandler(this.workMT_FailedWorkItem);
            this.workMT.AllWorkCompleted += new EventHandler(this.workMT_AllWorkCompleted);

            this.clientSoc.OnDeliverSmReq += new SmppDeliverSmHandler(this.clientSoc_OnDeliverSmReq);
            this.clientSoc.OnUnBindReq += new SmppUnBindHandler(this.clientSoc_OnUnBindReq);
            this.clientSoc.OnEnquireLinkReq += new SmppEnquireLinkHandler(this.clientSoc_OnEnquireLinkReq);

            try
            {
                this.nextRefreshTime = DateTime.Now;
                this.refreshInterval = TimeSpan.FromSeconds(0.2);
                this.statsMO = new int[6];
                this.workMO = new WorkQueue();
                this.workMO.ConcurrentLimit = 0x3e8;
                this.workMO.AllWorkCompleted += new EventHandler(this.workMO_AllWorkCompleted);
                this.workMO.WorkerException += new ResourceExceptionEventHandler(this.workMO_WorkerException);
                this.workMO.ChangedWorkItemState += new ChangedWorkItemStateEventHandler(this.workMO_ChangedWorkItemState);
            }
            catch { }

            try
            {
                this.nextRefreshTime = DateTime.Now;
                this.refreshInterval = TimeSpan.FromSeconds(0.2);
                this.statsMT = new int[6];
                this.workMT = new WorkQueue();
                this.workMT.ConcurrentLimit = 0x3e8;
                this.workMT.AllWorkCompleted += new EventHandler(this.workMT_AllWorkCompleted);
                this.workMT.WorkerException += new ResourceExceptionEventHandler(this.workMT_WorkerException);
                this.workMT.ChangedWorkItemState += new ChangedWorkItemStateEventHandler(this.workMT_ChangedWorkItemState);
            }
            catch { }
        }

        private void Gateway_Load(object sender, EventArgs e)
        {
            this.workMT.Pause();
        }

        private void InitSocket(ref SmppSock clientSoc)
        {
            try
            {
                clientSoc = new SmppSock((SmppSock.XY)SMPP);
                clientSoc.OnConnected += new ConnectDelegate(OnConnected);
                clientSoc.OnDisconnected += new DisconnectDelegate(OnDisconnected);
                clientSoc.OnReadyReceive += new ReceiveDelegate(OnReceive);
                clientSoc.OnReadySend += new SendDelegate(OnReadySend);
                clientSoc.OnConnectClose += new CloseDelegate(OnCloseRemote);
                clientSoc.OnSocketError += new SockErrDelegate(OnSocketError);
                clientSoc.OnSockMessage += new MessageDelegate(OnSockMessage);
                clientSoc.OnSockRequest += new RequestDelegate(OnSockRequest);
            }
            catch (Exception ex)
            {
                txtLog.Text = ex.Message;
            }
        }

        private void OnConnected(object sender, SmppSockEventArgs e)
        {
            clientSoc.SendBind(txtUser.Text, txtPwd.Text);
            clientSoc.ReceiveData();
        }

        private void OnDisconnected(object sender, SmppSockEventArgs e)
        {
            CloseSock();
        }

        private void OnSending(object sender, SmppSockEventArgs e)
        {
            this.txtLog.Text = e.SockMsg + " " + e.ByteSend.ToString() + " = " + Val.ToString();
        }

        private void OnSockMessage(object sender, SmppSockEventArgs e)
        {
            SmppSock sock = (SmppSock)sender;
            switch (sock.xy)
            {
                case SmppSock.XY.SMPP:
                    {
                        SMPPResponse resp = (SMPPResponse)e.response;
                        switch (resp.CommandID)
                        {
                            case PDU.CommandIDType.bind_transmitter_resp://bind
                                {
                                    BindOk(e.response);
                                } break;
                            case PDU.CommandIDType.submit_sm_resp://submit_sm
                                {
                                    SubmitOk(e.response);
                                } break;
                            default:
                                {
                                } break;
                        }
                    } break;
                case SmppSock.XY.SGIP:
                    {
                        SGIPResponse resp = (SGIPResponse)e.response;
                        switch (resp.CommandID)
                        {
                            case RoaminSGIP.PDU.PDU.CommandIDType.SGIP_BIND_RESP:// bind
                                {
                                    BindOk(e.response);
                                } break;
                            case RoaminSGIP.PDU.PDU.CommandIDType.SGIP_SUBMIT_RESP:// submit_sm
                                {
                                    SubmitOk(e.response);
                                } break;
                            default:
                                {
                                } break;
                        }
                    } break;
            }
            AddMsg(e.response, e.Msg);
        }

        private void OnSockRequest(object sender, SmppSockEventArgs e)
        {
            SmppSock sock = (SmppSock)sender;

            uint CommandLength = UnsignedNumConverter.SwapByteOrdering(System.BitConverter.ToUInt32(e.Msg, 0));
            uint CommandID = UnsignedNumConverter.SwapByteOrdering(System.BitConverter.ToUInt32(e.Msg, 4));
            uint CommandStatus = UnsignedNumConverter.SwapByteOrdering(System.BitConverter.ToUInt32(e.Msg, 8));
            uint SequenceNumber = UnsignedNumConverter.SwapByteOrdering(System.BitConverter.ToUInt32(e.Msg, 12));

            this.txtLog.Text = "Time:" + System.DateTime.Now.ToString() + ", Length: " + CommandLength.ToString() + ", ID: " + CommandID.ToString() + ", Status:" + CommandStatus.ToString() + ", SeqNo: " + SequenceNumber.ToString() + " - " + HexStringDisp(e.Msg);
            
            // Auto answer
            //if (checkBoxAutoAnwser.Checked)
            //{
            //    if (clientSoc.xy == SmppSock.XY.SMPP)
            //        SendSmppReponse(0x80000000 + CommandID, 0, SequenceNumber, null);
            //}
        }

        private void AddMsg(object response, byte[] buf)
        {
            string strLog = "";
            switch (clientSoc.xy)
            {
                case SmppSock.XY.SMPP:
                    {
                        SMPPResponse res = (SMPPResponse)response;
                        strLog = res.CommandLength.ToString() + " - " + res.CommandID.ToString() + " - " + res.CommandStatus.ToString() + " - " + res.SequenceNumber.ToString();
                    } break;
                default:
                    {
                        strLog = "";
                    } break;
            }

            this.txtLog.Text = strLog;
        }

        private string HexStringDisp(byte[] buf)
        {
            string str = "";
            for (int i = 0; i < buf.Length; i++)
            {
                str += buf[i].ToString("X2") + " ";
                if ((i + 1) % 4 == 0)
                {
                    str += "\r\n";
                }
            }
            return str;
        }

        private void Gateway_Resize(object sender, EventArgs e)
        {
            if (base.WindowState == FormWindowState.Minimized)
            {
                base.Hide();
            }
        }

        private void LoadMTQueue()
        {
            if (this.clientSoc.Connected)
            {
                if (this.ConnMT.State == ConnectionState.Closed)
                {
                    try
                    {
                        this.ConnMT.Open();
                    }
                    catch
                    {
                        this.WriteLog("Cannot connect to SMSC");
                        return;
                    }
                }

                this.timerMT.Stop();
                DataTable dt = null;
                try
                {
                    dt = MessageOutManager.GetMsgOut();
                }
                catch (Exception ex)
                {
                    this.WriteLog(DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss") + " Load MsgOut Error: " + ex.Message);
                    this.timerMT.Start();
                    return;
                }

                if (dt != null && dt.Rows.Count > 0)
                {
                    lock (this.workMT)
                    {
                        for (int i = 0; i < dt.Rows.Count; i++)
                        {
                            try
                            {
                                int num2 = Convert.ToInt32(dt.Rows[i]["MsgOutID"]);
                                string userID = dt.Rows[i]["MobileNo"].ToString();
                                string serviceID = dt.Rows[i]["ServiceID"].ToString();
                                string messageInfo = dt.Rows[i]["MsgOutContent"].ToString();
                                int contentType = Convert.ToInt32(dt.Rows[i]["ContentType"]);
                                this.workMT.Add(new MOWorkItem(userID, serviceID, messageInfo, contentType, this));
                            }
                            catch (Exception ex1)
                            {
                                this.WriteLog("Add MsgOut Queue Error: " + ex1.Message);
                            }
                        }
                    }
                }
                this.timerMT.Start();
            }
        }

        private void AddMsgInWorkItem(SMPPDeliverSM MsgIn)
        {
            if (MsgIn.ShortMessage.ToString().IndexOf("stat:") == -1)
            {
                lock (this.workMO)
                {
                    this.workMO.Add(new MsgInWorkItem(MsgIn, this));
                }

                if (ConfigApp.bLogMO)
                    this.WriteLog("Receive MO - RequestID=" + MsgIn.SequenceNumber.ToString() + " from " + MsgIn.SourceAddress + " to " + MsgIn.DestAddress + " Content: " + MsgIn.ShortMessage.ToString());
            }
            else if (ConfigApp.bLogMO)
            {
                this.WriteLog(string.Concat(new object[] { "Receive Dev - CommandStatus=", MsgIn.CommandStatus, " RequestID", MsgIn.SequenceNumber.ToString(), " from ", MsgIn.SourceAddress, " to ", MsgIn.DestAddress, " Content: ", MsgIn.ShortMessage.ToString() }));
            }
        }

        private void workMO_FailedWorkItem(object sender, WorkItemEventArgs e)
        {
            if (ConfigApp.bLogMO)
            {
                this.WriteLog("MOWorkQueue Error: " + e.WorkItem.FailedException.Message);
            }
        }

        private void workMO_WorkerException(object sender, ResourceExceptionEventArgs e)
        {
            this.WriteLog(e.Exception.Message);
        }

        public void WriteLog(string msg)
        {
            lock (this.txtLog)
            {
                if ((this.txtLog.TextLength + msg.Length) > 0x8000)
                {
                    this.txtLog.Clear();
                }
                this.txtLog.AppendText(msg + Environment.NewLine);
            }
        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            base.Close();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            if (btnStart.Text == "&Start")
            {
                this.timerEN.Enabled = true;
                this.timerMT.Enabled = true;
                this.btnStart.Text = "&Stop";
            }
            else
            {
                this.timerEN.Enabled = false;
                this.timerMT.Enabled = false;
                this.btnStart.Text = "&Start";
            }
        }

        private void getCountMO()
        {
            try
            {
                if (base.InvokeRequired)
                {
                    MethodInvoker method = new MethodInvoker(this.getCountMO);
                    base.BeginInvoke(method);
                }
                else
                {
                    lock (this)
                    {
                        this.lblMOSchedule.Text = this.statsMO[1].ToString("N0");
                        this.lblMOQueue.Text = this.statsMO[2].ToString("N0");
                        this.lblMORunning.Text = this.statsMO[3].ToString("N0");
                        this.lblMOError.Text = this.statsMO[4].ToString("N0");
                        this.lblMOSuccess.Text = this.statsMO[5].ToString("N0");
                    }
                }
            }
            catch { }
        }

        private void getCountMT()
        {
            try
            {
                if (base.InvokeRequired)
                {
                    MethodInvoker method = new MethodInvoker(this.getCountMT);
                    base.BeginInvoke(method);
                }
                else
                {
                    lock (this)
                    {
                        this.lblMTSchedule.Text = this.statsMT[1].ToString("N0");
                        this.lblMTQueue.Text = this.statsMT[2].ToString("N0");
                        this.lblMTRunning.Text = this.statsMT[3].ToString("N0");
                        this.lblMTError.Text = this.statsMT[4].ToString("N0");
                        this.lblMTSuccess.Text = this.statsMT[5].ToString("N0");
                    }
                }
            }
            catch { }
        }

        private void btnReset_Click(object sender, EventArgs e)
        {
            lock (this.txtLog)
            {
                this.txtLog.Text = "";
            }

            try
            {
                // Reset MO
                lock (this.statsMO)
                {
                    for (int i = 0; i < this.statsMO.Length; i++)
                    {
                        this.statsMO[i] = 0;
                    }
                }

                txtTotalMO.Text = "0";
                this.getCountMO();

                // Reset MT
                lock (this.statsMT)
                {
                    for (int i = 0; i < this.statsMT.Length; i++)
                    {
                        this.statsMT[i] = 0;
                    }
                }

                txtTotalMT.Text = "0";
                this.getCountMT();
            }
            catch { }
        }

        private void EnquireLinkReq()
        {
            this.timerEN.Stop();
            try
            {
                SMPPEnquireLinkResponse res;
                lock (this.clientSoc)
                {
                    res = this.clientSoc.EnquireLink();
                }
                if (res.CommandStatus == 0)
                {
                    if (ConfigApp.bLogEn)
                        this.WriteLog("Send EnquireLink SeqID " + res.SequenceNumber);
                }
                else
                {
                    if (ConfigApp.bLogEn)
                        this.WriteLog("Send EnquireLink SeqID " + res.SequenceNumber + " Error");
                    
                    if (AppStatus.IsBind)
                    {
                        DateTime now = DateTime.Now;
                        this.clientSoc.CloseSocket();
                        while (!AppStatus.IsConnect)
                        {
                            if (DateTime.Now > now)
                            {
                                this.ReBin();
                                now = now.AddSeconds(3.0);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (ConfigApp.bLogEn)
                    this.WriteLog("Send Enquire Link Error: " + ex.Message);
                
                if (AppStatus.IsBind)
                {
                    DateTime time2 = DateTime.Now;
                    this.clientSoc.CloseSocket();
                    while (!AppStatus.IsConnect)
                    {
                        if (DateTime.Now > time2)
                        {
                            this.ReBin();
                            time2 = time2.AddSeconds(3.0);
                        }
                    }
                }
            }
            this.timerEN.Start();
        }

        private void timerMT_Tick(object sender, EventArgs e)
        {
            this.LoadMTQueue();
        }

        private void timerEN_Tick(object sender, EventArgs e)
        {
            if (clientSoc != null)
            {
                if (clientSoc.Soc.Connected)
                {
                    clientSoc.CloseSocket();
                }
                txtLog.Text = "Started";
                return;
            }
            txtLog.Text = "Running...";
            int nPort = Convert.ToInt32(txtCPort.Text);
            IPEndPoint ipLocalEndPoint;
            try
            {
                IPAddress ipAddress = IPAddress.Parse(txtIP.Text);

                ipLocalEndPoint = new IPEndPoint(ipAddress, nPort);
            }
            catch (SocketException socErr)
            {
                MessageBox.Show(socErr.Message);
                return;
            }

            try
            {
                if (clientSoc == null)
                {
                    this.InitSocket(ref clientSoc);
                }

                if (!clientSoc.Soc.Connected)
                {
                    clientSoc.AsyConnectTCIP(ipLocalEndPoint); ;
                }
            }
            catch (Exception ex)
            {
                txtLog.Text = "Connect Button :" + ex.Message;
                this.InitSocket(ref clientSoc);
            }
        }

        private void AddMsgOutWorkItem(SMPPDeliverSM MsgOut)
        {
            if (MsgOut.ShortMessage.ToString().IndexOf("stat:") == -1)
            {
                lock (this.workMT)
                {
                    this.workMT.Add(new AddMsgOutWorkItem(MsgOut, this));
                }

                if (ConfigApp.bLogMT)
                    this.WriteLog("Receive MsgOut - RequestID=" + MsgOut.SequenceNumber.ToString() + " from " + MsgOut.SourceAddress + " to " + MsgOut.DestAddress + " Content: " + MsgOut.ShortMessage.ToString());
            }
            else if (ConfigApp.bLogMT)
                this.WriteLog(string.Concat(new object[] { "Receive Dev - CommandStatus=", MsgOut.CommandStatus, " RequestID", MsgOut.SequenceNumber.ToString(), " from ", MsgOut.SourceAddress, " to ", MsgOut.DestAddress, " Content: ", MsgOut.ShortMessage.ToString() }));
        }

        private void workMO_ChangedWorkItemState(object sender, ChangedWorkItemStateEventArgs e)
        {
            try
            {
                lock (this)
                {
                    IntPtr ptr;
                    IntPtr ptr2;
                    this.statsMO[(int)(ptr = (IntPtr)e.PreviousState)] = this.statsMO[(int)ptr] - 1;
                    this.statsMO[(int)(ptr2 = (IntPtr)e.WorkItem.State)] = this.statsMO[(int)ptr2] + 1;
                }
                if (DateTime.Now > this.nextRefreshTime)
                {
                    this.getCountMO();
                    this.nextRefreshTime = DateTime.Now + this.refreshInterval;
                }
            }
            catch { }
        }

        private void workMO_AllWorkCompleted(object sender, EventArgs e)
        {
            try
            {
                if (this.InvokeRequired)
                    this.Invoke(new EventHandler(this.workMO_AllWorkCompleted), new object[] { sender, e });
                
                else
                {
                    this.getCountMO();
                    this.statsMO = new int[6];
                    this.Cursor = Cursors.Arrow;
                }
            }
            catch { }
        }

        private void workMT_ChangedWorkItemState(object sender, ChangedWorkItemStateEventArgs e)
        {
            try
            {
                lock (this)
                {
                    IntPtr ptr;
                    IntPtr ptr2;
                    this.statsMT[(int)(ptr = (IntPtr)e.PreviousState)] = this.statsMT[(int)ptr] - 1;
                    this.statsMT[(int)(ptr2 = (IntPtr)e.WorkItem.State)] = this.statsMT[(int)ptr2] + 1;
                }

                if (DateTime.Now > this.nextRefreshTime)
                {
                    this.getCountMT();
                    this.nextRefreshTime = DateTime.Now + this.refreshInterval;
                }
            }
            catch {}
        }

        private void workMT_WorkerException(object sender, ResourceExceptionEventArgs e)
        {
            Application.OnThreadException(e.Exception);
        }

        private void workMT_AllWorkCompleted(object sender, EventArgs e)
        {
            try
            {
                if (base.InvokeRequired)
                    base.Invoke(new EventHandler(this.workMT_AllWorkCompleted), new object[] { sender, e });
                
                else
                {
                    this.getCountMT();
                    this.statsMT = new int[6];
                    this.Cursor = Cursors.Arrow;
                }
            }
            catch { }
        }

        private void workMT_FailedWorkItem(object sender, WorkItemEventArgs e) { }

        private void chkEnquireLink_CheckedChanged(object sender, EventArgs e)
        {
            ConfigApp.bLogEn = this.chkEnquireLink.Checked;
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            new SendSms(this).Show();
        }

        private void chkLogMO_CheckedChanged(object sender, EventArgs e)
        {
            ConfigApp.bLogMO = this.chkLogMO.Checked;
        }

        private void chkLogMT_CheckedChanged(object sender, EventArgs e)
        {
            ConfigApp.bLogMT = this.chkLogMT.Checked;
        }

        private void ResendMOMT()
        {
            try
            {
                bool result = MessageOutManager.Update_MsgInOut_Loading();
            }
            catch { }

        }

        public SMPPSubmitSM SubmitSm(SMPPSubmitSM MTSubmit)
        {
            lock (this.clientSoc)
            {
                return this.clientSoc.SubmitMsg(MTSubmit.SourceSubaddress, MTSubmit.DestSubaddress, false, MTSubmit.MessagePayload);
            }
        }

        // Properties
        public WorkQueue MsgOutWorkQueue
        {
            get { return this.workMT; }
        }

        public SmppSock clientSoc
        {
            get { return this.clientSoc; }
        }
    }
}